/*!	 base_types.cpp
**	 Template Header
**
**	......... ... 2014 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <cstdlib>

#include "base_types.h"

#include "general.h"
#include <synfig/localization.h>

#include "value.h"
#include "canvas.h"
#include "valuenodes/valuenode_bone.h"
#include "gradient.h"
#include "bone.h"
#include "matrix.h"
#include "boneweightpair.h"
#include "transformation.h"
#include "vector.h"
#include "time.h"
#include "segment.h"
#include "color.h"
#include "blinepoint.h"
#include "widthpoint.h"
#include "dashitem.h"

#endif

using namespace synfig;
using namespace types_namespace;
using namespace etl;

#define TRY_FIX_FOR_BUG_27

namespace synfig
{
namespace types_namespace
{

// Bool

class TypeBool: public synfig::Type
{
    static String to_string(const bool &x)
    {
        return etl::strprintf("Bool (%s)", x ? "true" : "false");
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "bool";
        description.aliases.push_back(_("bool"));
        description.local_name = N_("bool");
        register_all<bool, to_string>();
    }
public:
    static TypeBool instance;
};
TypeBool TypeBool::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(bool, TypeBool)

// Integer

class TypeInteger: public Type
{
    static String to_string(const int &x)
    {
        return etl::strprintf("Integer (%d)", x);
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "integer";
        description.aliases.push_back("int");
        description.aliases.push_back(_("integer"));
        description.local_name = N_("integer");
        register_all<int, to_string>();
    }
public:
    static TypeInteger instance;
};
TypeInteger TypeInteger::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(int, TypeInteger)

// Angle

class TypeAngle: public Type
{
    static String to_string(const Angle &x)
    {
        return etl::strprintf("Angle (%.2f)", Angle::deg(x).get());
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "angle";
        description.aliases.push_back("degrees");
        description.aliases.push_back("radians");
        description.aliases.push_back("rotations");
        description.local_name = N_("angle");
        register_all<Angle, to_string>();
    }
public:
    static TypeAngle instance;
};
TypeAngle TypeAngle::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(Angle, TypeAngle)

// Real

class TypeReal: public Type
{
public:
    class Inner
    {
    public:
        mutable float f;
        mutable Time t;
        Real r;
        Inner(): f(0.f), t(0.0), r(0.0) { }

        bool operator== (const Inner &other) const
        {
            return r == other.r;
        }
        Inner& operator= (const Inner &other)
        {
            return *this = other.r;
        }

        Inner& operator= (const Real &other)
        {
            r = other;
            return *this;
        }
        operator const Real&() const
        {
            return r;
        }

        Inner& operator= (const float &other)
        {
            r = other;
            return *this;
        }
        operator const float&() const
        {
            return f = r;
        }

        Inner& operator= (const Time &other)
        {
            r = other;
            return *this;
        }
        operator const Time&() const
        {
            return t = r;
        }
    };
private:
    static bool equal(ConstInternalPointer a, ConstInternalPointer b)
    {
        return abs((*(Inner*)a).r - (*(Inner*)b).r) <= 1e-14;
    }
    static bool less(ConstInternalPointer a, ConstInternalPointer b)
    {
        return !equal(a, b) && (*(Inner*)a).r < (*(Inner*)b).r;
    }
    static String to_string(const Inner &x)
    {
        return etl::strprintf("Real (%f)", (Real)x);
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "real";
        description.aliases.push_back("float");
        description.aliases.push_back(_("real"));
        description.local_name = N_("real");
        register_all_but_compare<Inner, Real, to_string>();
        register_alias<Inner, float>();
        register_alias<Inner, Time>();
        register_equal(equal);
        register_less(less);
    }
public:
    static TypeReal instance;
};
TypeReal TypeReal::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(Real, TypeReal)
SYNFIG_IMPLEMENT_TYPE_ALIAS(float, TypeReal)

// Time

class TypeTime: public Type
{
    typedef TypeReal::Inner Inner;
    static String to_string(const Inner &x)
    {
        return etl::strprintf("Time (%s)", ((const Time&)x).get_string().c_str());
    }
    static bool equal(ConstInternalPointer a, ConstInternalPointer b)
    {
        return (const Time&) * (Inner*)a == (const Time&) * (Inner*)b;
    }
    static bool less(ConstInternalPointer a, ConstInternalPointer b)
    {
        return !equal(a, b) && (const Time&) * (Inner*)a < (const Time&) * (Inner*)b;
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        TypeReal::instance.initialize();
        description.name = "time";
        description.aliases.push_back(_("time"));
        description.local_name = N_("time");
        register_all_but_compare<Inner, Time, to_string>();
        register_alias<Inner, float>();
        register_alias<Inner, Real>();
        register_equal(equal);
        register_less(less);
        register_copy(identifier, TypeReal::instance.identifier, Operation::DefaultFuncs::copy<Inner>);
        register_copy(TypeReal::instance.identifier, identifier, Operation::DefaultFuncs::copy<Inner>);
    }
public:
    static TypeTime instance;
};
TypeTime TypeTime::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(Time, TypeTime)

// Vector

class TypeVector: public Type
{
    static String to_string(const Vector &x)
    {
        return etl::strprintf("Vector (%f, %f)", x[0], x[1]);
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "vector";
        description.aliases.push_back("point");
        description.local_name = N_("vector");
        register_all<Vector, to_string>();
    }
public:
    static TypeVector instance;
};
TypeVector TypeVector::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(Vector, TypeVector)

// Color

class TypeColor: public Type
{
    static String to_string(const Color &x)
    {
        return etl::strprintf("Color (%s)", x.get_string().c_str());
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "color";
        description.local_name = N_("color");
        register_all<Color, to_string>();
    }
public:
    static TypeColor instance;
};
TypeColor TypeColor::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(Color, TypeColor)

// Segment

class TypeSegment: public Type
{
    static String to_string(const Segment &x)
    {
        return etl::strprintf("Segment ((%f, %f) to (%f, %f))", x.p1[0], x.p1[1], x.p2[0], x.p2[1]);
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "segment";
        description.local_name = N_("segment");
        register_all_but_compare<Segment, to_string>();
    }
public:
    static TypeSegment instance;
};
TypeSegment TypeSegment::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(Segment, TypeSegment)

// BLinePoint

class TypeBLinePoint: public Type
{
    static String to_string(const BLinePoint &x)
    {
        return etl::strprintf("BLinePoint (%f, %f)", x.get_vertex()[0], x.get_vertex()[1]);
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "bline_point";
        description.aliases.push_back("blinepoint");
        description.local_name = N_("spline_point");
        register_all_but_compare<BLinePoint, to_string>();
    }
public:
    static TypeBLinePoint instance;
};
TypeBLinePoint TypeBLinePoint::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(BLinePoint, TypeBLinePoint)

// Matrix

class TypeMatrix: public Type
{
    static String to_string(const Matrix &x)
    {
        return etl::strprintf("Matrix (%s)", x.get_string().c_str());
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "matrix";
        description.local_name = N_("matrix");
        register_all_but_compare<Matrix, to_string>();
    }
public:
    static TypeMatrix instance;
};
TypeMatrix TypeMatrix::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(Matrix, TypeMatrix)

// BoneWeightPair

class TypeBoneWeightPair: public Type
{
    static String to_string(const BoneWeightPair &x)
    {
        return etl::strprintf("Bone Weight Pair (%s)", x.get_string().c_str());
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "bone_weight_pair";
        description.local_name = N_("bone_weight_pair");
        register_all_but_compare<BoneWeightPair, to_string>();
    }
public:
    static TypeBoneWeightPair instance;
};
TypeBoneWeightPair TypeBoneWeightPair::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(BoneWeightPair, TypeBoneWeightPair)

// WidthPoint

class TypeWidthPoint: public Type
{
    static String to_string(const WidthPoint &x)
    {
        return etl::strprintf("WidthPoint (%f, %f)", x.get_position(), x.get_width());
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "width_point";
        description.aliases.push_back("widthpoint");
        description.local_name = N_("width_point");
        register_all_but_compare<WidthPoint, to_string>();
    }
public:
    static TypeWidthPoint instance;
};
TypeWidthPoint TypeWidthPoint::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(WidthPoint, TypeWidthPoint)

// DashItem

class TypeDashItem: public Type
{
    static String to_string(const DashItem &x)
    {
        return etl::strprintf("DashItem (%f, %f)", x.get_offset(), x.get_length());
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "dash_item";
        description.aliases.push_back("dashitem");
        description.local_name = N_("dash_item");
        register_all<DashItem, to_string>();
    }
public:
    static TypeDashItem instance;
};
TypeDashItem TypeDashItem::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(DashItem, TypeDashItem)

// List

class TypeList: public Type
{
    static String to_string(const ValueBase::List &x)
    {
        return etl::strprintf("List (%d elements)", x.size());
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "list";
        description.local_name = N_("list");
        register_all<ValueBase::List, to_string>();
    }
public:
    static TypeList instance;
};
TypeList TypeList::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(ValueBase::List, TypeList)

// Canvas

class TypeCanvas: public Type
{
    class Inner
    {
    public:
        typedef Canvas *CanvasPtr;

#ifdef TRY_FIX_FOR_BUG_27
        bool fake_handle;
#endif
        etl::handle<Canvas> h;
        etl::loose_handle<Canvas> lh;
        mutable CanvasPtr p;
#ifdef TRY_FIX_FOR_BUG_27
        Inner(): fake_handle(false), p(NULL) { }
        ~Inner()
        {
            if (fake_handle) {
                h->ref();
            }
        }
#else
        Inner(): p(NULL) { }
#endif
        Inner& operator= (const etl::loose_handle<Canvas> &other)
        {
#ifdef TRY_FIX_FOR_BUG_27

            if (fake_handle) {
                h->ref();
            }

#endif
            lh = other;
            h = other;
#ifdef TRY_FIX_FOR_BUG_27
            fake_handle = h && !h->is_inline();

            if (fake_handle) {
                h->unref_inactive();
            }

#endif
            return *this;
        }
        Inner& operator= (const etl::handle<Canvas> &other)
        {
            return *this = etl::loose_handle<Canvas>(other);
        }
        Inner& operator= (const CanvasPtr &other)
        {
            return *this = etl::loose_handle<Canvas>(other);
        }
        Inner& operator= (const Inner &other)
        {
            return *this = other.lh;
        }
        bool operator== (const Inner &other) const
        {
            return lh == other.lh;
        }

        operator const etl::loose_handle<Canvas>&() const
        {
            return lh;
        }
        operator const etl::handle<Canvas>&() const
        {
            return h;
        }
        operator const CanvasPtr &() const
        {
            return p = &*lh;
        }
    };
    static String to_string(const Inner &x)
    {
        return etl::strprintf("Canvas (%s)", x.lh ? x.lh->get_id().c_str() : "NULL");
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "canvas";
        description.local_name = N_("canvas");
        register_all<Inner, etl::loose_handle<Canvas>, to_string>();
        register_alias< Inner, etl::handle<Canvas> >();
        register_alias<Inner, Canvas*>();
    }
public:
    static TypeCanvas instance;
};
TypeCanvas TypeCanvas::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(etl::loose_handle<Canvas>, TypeCanvas)
SYNFIG_IMPLEMENT_TYPE_ALIAS(etl::handle<Canvas>, TypeCanvas)
SYNFIG_IMPLEMENT_TYPE_ALIAS(Canvas*, TypeCanvas)

// String

class TypeString: public Type
{
    class Inner: public String
    {
        mutable const char *c;
    public:
        operator const char * const &() const
        {
            return c = c_str();
        }
        String& operator=(const String &x)
        {
            std::string::operator=(x);
            return *this;
        }
        String& operator=(const char * const &x)
        {
            std::string::operator=(x);
            return *this;
        }
    };
    static String to_string(const Inner &x)
    {
        return etl::strprintf("String (%s)", x.c_str());
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "string";
        description.local_name = N_("string");
        register_all<Inner, String, to_string>();
        register_alias<Inner, const char*>();
    }
public:
    static TypeString instance;
};
TypeString TypeString::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(String, TypeString)
SYNFIG_IMPLEMENT_TYPE_ALIAS(const char*, TypeString)

// Gradient

class TypeGradient: public Type
{
    static String to_string(const Gradient &x)
    {
        return etl::strprintf("Gradient (%d cpoints)", x.size());
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "gradient";
        description.local_name = N_("gradient");
        register_all_but_compare<Gradient, to_string>();
    }
public:
    static TypeGradient instance;
};
TypeGradient TypeGradient::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(Gradient, TypeGradient)

// Bone

class TypeBoneObject: public Type
{
    static String to_string(const Bone &x)
    {
        return etl::strprintf("Bone (%s)", x.get_string().c_str());
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "bone_object";
        description.local_name = N_("bone_object");
        register_all_but_compare<Bone, to_string>();
    }
public:
    static TypeBoneObject instance;
};
TypeBoneObject TypeBoneObject::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(Bone, TypeBoneObject)

// BoneValueNode

class TypeBoneValueNode: public Type
{
    class Inner
    {
    public:
        typedef ValueNode_Bone *ValueNode_BonePtr;

        etl::handle<ValueNode_Bone> h;
        mutable etl::loose_handle<ValueNode_Bone> lh;
        mutable ValueNode_BonePtr p;

        Inner(): p(NULL) { }
        Inner& operator= (const etl::handle<ValueNode_Bone> &other)
        {
            h = other;
            return *this;
        }
        Inner& operator= (const etl::loose_handle<ValueNode_Bone> &other)
        {
            h = other;
            return *this;
        }
        Inner& operator= (const ValueNode_BonePtr &other)
        {
            h = other;
            return *this;
        }
        Inner& operator= (const Inner &other)
        {
            return *this = other.h;
        }
        bool operator== (const Inner &other) const
        {
            return h == other.h;
        }

        operator const ValueNode_Bone::Handle&() const
        {
            return h;
        }
        operator const ValueNode_Bone::LooseHandle&() const
        {
            return lh = h;
        }
        operator const ValueNode_BonePtr &() const
        {
            return p = &*h;
        }
    };
    static String to_string(const Inner &x)
    {
        return etl::strprintf("ValueNodeBone (%s)", x.lh ? x.lh->get_string().c_str() : "NULL");
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "bone_valuenode";
        description.local_name = N_("bone_valuenode");
        register_all<Inner, etl::loose_handle<ValueNode_Bone>, to_string>();
        register_alias< Inner, etl::handle<ValueNode_Bone> >();
        register_alias<Inner, ValueNode_Bone*>();
    }
public:
    static TypeBoneValueNode instance;
};
TypeBoneValueNode TypeBoneValueNode::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(etl::handle<ValueNode_Bone>, TypeBoneValueNode)
SYNFIG_IMPLEMENT_TYPE_ALIAS(etl::loose_handle<ValueNode_Bone>, TypeBoneValueNode)
SYNFIG_IMPLEMENT_TYPE_ALIAS(ValueNode_Bone*, TypeBoneValueNode)

// Transformation

class TypeTransformation: public Type
{
    static String to_string(const Transformation &x)
    {
        return etl::strprintf("Transformation (%f, %f) (%f) (%f, %f)", x.offset[0], x.offset[1], Angle::deg(x.angle).get(), x.scale[0], x.scale[1]);
    }
    void initialize_vfunc(Description &description)
    {
        Type::initialize_vfunc(description);
        description.name = "transformation";
        description.local_name = N_("transformation");
        register_all<Transformation, to_string>();
    }
public:
    static TypeTransformation instance;
};
TypeTransformation TypeTransformation::instance;
SYNFIG_IMPLEMENT_TYPE_ALIAS(Transformation, TypeTransformation)

}
} // END of namespaces types_namespace and synfig

namespace synfig
{
Type &type_bool				= TypeBool::instance;
Type &type_integer			= TypeInteger::instance;
Type &type_angle			= TypeAngle::instance;
Type &type_time				= TypeTime::instance;
Type &type_real				= TypeReal::instance;
Type &type_vector			= TypeVector::instance;
Type &type_color			= TypeColor::instance;
Type &type_segment			= TypeSegment::instance;
Type &type_bline_point		= TypeBLinePoint::instance;
Type &type_matrix			= TypeMatrix::instance;
Type &type_bone_weight_pair	= TypeBoneWeightPair::instance;
Type &type_width_point		= TypeWidthPoint::instance;
Type &type_dash_item		= TypeDashItem::instance;
Type &type_list				= TypeList::instance;
Type &type_canvas			= TypeCanvas::instance;
Type &type_string			= TypeString::instance;
Type &type_gradient			= TypeGradient::instance;
Type &type_bone_object		= TypeBoneObject::instance;
Type &type_bone_valuenode	= TypeBoneValueNode::instance;
Type &type_transformation	= TypeTransformation::instance;
}; // END of namespace synfig